import React, { useEffect, useRef, useImperativeHandle } from 'react'
import BScroll from '@better-scroll/core'
import Pullup from '@better-scroll/pull-up'
import styled from 'styled-components'

/**
    <div class="wrapper">
        <ul class="content">
            <li>...</li>
            <li>...</li>
            ...
        </ul>
        <!-- you can put some other DOMs here, it won't affect the scrolling -->
    </div>
 * In the code above, BetterScroll is applied to the outer wrapper container, and the scrolling part is content element.
 * Pay attention that BetterScroll handles the scroll of the first child element (content) of the container (wrapper) by default, which means other elements will be ignored.
 * https://better-scroll.github.io/docs/en-US/guide/#getting-started
 */
BScroll.use(Pullup)

const Wrapper = styled.div`
    display: flex;
    flex-direction: column;
    width: 100%;
    height: 100%;
    min-height: 0;
    position: relative;
    
`
const Content = styled.div`
    
`

interface DataSource {
    className?: string;
    children?: React.ReactNode;
    fetchData: () => Promise<boolean>
}

type DataActions = {
    reset: VoidFunction
}

const ScrollView: React.FC<DataSource & React.RefAttributes<DataActions>> = React.forwardRef<DataActions, DataSource>(({ children, className, fetchData }, ref) => {
    const wapperRef = useRef<HTMLDivElement>(null)
    const contentRef = useRef<HTMLDivElement>(null)
    const more = useRef<boolean>(true)
    const heightRef = useRef<number>(0)

    useImperativeHandle(ref, () => ({
        reset: () => {
            more.current = true
        }
    }))

    useEffect(() => {
        const dom = wapperRef.current;
        const content = contentRef.current
        if (!dom || !content || !fetchData) {
            return
        }

        const resizeObserver = new ResizeObserver(entries => {
            for (const entry of entries) {
                if (heightRef.current !== entry.contentRect.height) {
                    heightRef.current = entry.contentRect.height
                    bs?.refresh()
                }

            }
        });
        resizeObserver.observe(content);

        const bs = new BScroll(dom, { bounce: true, click: true, pullUpLoad: true, stopPropagation: true });
        bs.enable();
        bs.openPullUp({
            threshold: 5
        })
        bs.on('pullingUp', async () => {
            if (more.current === false) {
                bs.finishPullUp()
                return
            }

            more.current = await fetchData();
            bs.finishPullUp();
            bs.refresh();
        })

        return () => {
            bs.destroy()
            resizeObserver.disconnect()
        }
    }, [fetchData])

    useEffect(() => {
        fetchData().then(res => more.current = res)
    }, [fetchData])

    return <Wrapper
        ref={wapperRef}
    >
        <Content
            className={className}
            ref={contentRef}
        >
            {children}
        </Content>
    </Wrapper>
})

export default ScrollView